#pragma once

#include <iostream>
#include <string>

#include "httplib.h"
#include "exception.hpp"
#include "request/add_contact_request.pb.h"
#include "responce/add_contact_responce.pb.h"
#include "request/find_one_request.pb.h"
#include "responce/find_one_responce.pb.h"
#include "request/del_contact_request.pb.h"
#include "responce/del_contact_responce.pb.h"
#include "responce/people_info.pb.h"
#include "responce/find_all_responce.pb.h"

#define SERVER_ADDRESS "106.55.180.125"
#define SERVER_PORT 8888

using std::cout;
using std::cin;
using std::endl;
using namespace add_contact_req;
using namespace add_contact_res;

namespace web_contacts
{
    //通讯录客户端业务功能
    class ContactClient
    {
    public:
        //添加联系人
        void addContact()
        {
            //创建联系人
            AddContactRequest contact;
            buildContace(contact);
            //序列化
            std::string req_str;
            if(!contact.SerializePartialToString(&req_str))
                throw ContactException("addContact Serialize error!");
            //构造客户端，并发起post请求
            httplib::Client client(SERVER_ADDRESS, SERVER_PORT);
            auto res = client.Post("/contact/add", req_str, "application/protobuf");
            if(!res)//链接失败
            {
                std::string err_desc;
                err_desc.append("/contact/add 链接失败, 错误信息: ")
                        .append(httplib::to_string(res.error()));
                throw ContactException(err_desc);
            }
            //反序列化
            AddContactResponce resp;
            bool parse = resp.ParseFromString(res->body);
            //判断结果
            if(res->status != 200 && parse == false)
            {
                //服务端处理数据异常，客户端反序列化也异常
                std::string err_desc;
                err_desc.append("post '/contact/add' and parse 失败 ")
                        .append(std::to_string(res->status))
                        .append(" (").append(res->reason)
                        .append(")");
                throw ContactException(err_desc);
            }
            else if(res->status != 200)
            {
                //服务端处理数据异常
                std::string err_desc;
                err_desc.append("post '/contact/add 失败 '")
                        .append(std::to_string(res->status))
                        .append(" (").append(res->reason)
                        .append(") 错误原因: ")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            else if(resp.base_resp().success() == 0)
            {
                //服务端出错
                std::string err_desc;
                err_desc.append("post '/contact/add' 出错, 错误原因:")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            
            //到达此处是正常添加
            cout << "--->添加联系人成功, uuid: " << resp.uid() << endl;
        }

        //删除联系人
        void delContact()
        {
            //获取要删除的联系人的uuid
            cout << "--->请输入删除的联系人的uuid: ";
            std::string uuid;
            getline(cin, uuid);
            //构造请求并序列化
            del_contact_req::DelContactRequest req;
            req.set_uuid(uuid);
            std::string req_str;
            if(!req.SerializeToString(&req_str))
                throw ContactException("delete Contact request serialize error!\n");
            //构造客户端
            httplib::Client client(SERVER_ADDRESS, SERVER_PORT);
            auto res = client.Post("/contact/del", req_str, "application/protobuf");
            if(!res)
            {
                std::string err_desc;
                err_desc.append("链接 '/contact/del' 失败, 错误原因: ")
                        .append(httplib::to_string(res.error()));
                throw ContactException(err_desc);
            }
            //判断服务是否成功
            //获取响应的protobuf
            del_contact_resp::DelContactResponce resp;
            auto parse = resp.ParseFromString(res->body);
            if(res->status != 200 && !parse)//服务失败，反序列化也失败
            {
                std::string err_desc;
                err_desc.append("/contact/del 服务失败 && 响应反序列失败, 状态码: ")
                        .append(std::to_string(res->status))
                        .append("(").append(res->reason).append(")");
                throw ContactException(err_desc);
            }
            else if(res->status != 200)//服务失败
            {
                std::string err_desc;
                err_desc.append("/contact/del 服务失败, 状态码: ")
                        .append(std::to_string(res->status))
                        .append("(").append(res->reason).append(") 错误原因: ")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            else if(resp.base_resp().success() != true)
            {
                std::string err_desc;
                err_desc.append("/contact/del 结果异常, 错误原因")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            //到此是正常返回
            cout << "--->删除成功, 删除联系人信息" << endl;
            printDelContactInfo(resp);
        }
        //查找联系人
        void findOne()
        {
            cout << "请输入查找的联系人姓名: ";
            std::string name;
            cin >> name;
            //序列化
            find_one_req::FindOneRequest req;
            req.set_name(name);
            std::string req_str;
            if(!req.SerializeToString(&req_str))
                throw ContactException("findOne req Serialize error!\n");
            //构建客户端连接服务器
            httplib::Client client(SERVER_ADDRESS, SERVER_PORT);
            auto res = client.Post("/contact/find-one", req_str, "application/protobuf");
            if(!res)//连接失败
            {
                std::string err_desc;
                err_desc.append("/contact/find-one 连接失败, 错误信息: ")
                        .append(httplib::to_string(res.error()));
                throw ContactException(err_desc);
            }

            //判断响应是否正确
            find_one_res::FindOneRespone resp;
            bool parse = resp.ParseFromString(res->body);
            if(res->status != 200 && !parse)//服务失败，反序列化也失败
            {
                std::string err_desc;
                err_desc.append("post '/contact/find-one' 失败, 反序列化响应失败 ")
                        .append(std::to_string(res->status))
                        .append("(").append(res->reason).append(")");
                throw ContactException(err_desc);
            }
            else if(res->status != 200)//服务失败
            {
                std::string err_desc;
                err_desc.append("post '/contact/find-one' 失败 ")
                        .append(std::to_string(res->status))
                        .append("(").append(res->reason)
                        .append(") 错误原因: ")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            else if(!resp.base_resp().success())//结果异常
            {
                std::string err_desc;
                err_desc.append("post /contact/find-one 结果异常: ")
                        .append("错误原因: ").append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            //正常返回，打印结果
            cout << "--->查询成功" << endl;
            cout << "联系人 " << name << " uuid: " << resp.uuid() << endl << endl;
        }
        //展示所有联系人
        void findAll()
        {
            find_all_resp::FindAllResponce resp;
            //直接向服务端请求
            httplib::Client client(SERVER_ADDRESS, SERVER_PORT);
            auto res = client.Get("/contact/find-all");
            if(!res){
                std::string err_desc;
                err_desc.append("get '/contact/find-all' 失败, 错误原因: ")
                        .append(httplib::to_string(res.error()));
                throw ContactException(err_desc);
            }
            auto parse = resp.ParseFromString(res->body);
            if(res->status != 500 && !parse){
                std::string err_desc;
                err_desc.append("get '/contact/find-all' 失败且响应反序列化失败, 状态码: ")
                        .append(std::to_string(res->status)).append("(")
                        .append(res->reason).append(")");
                throw ContactException(err_desc);
            }
            else if(res->status != 200){
                std::string err_desc;
                err_desc.append("get '/contact/find-all' 失败 状态码: ")
                        .append(std::to_string(res->status)).append("(")
                        .append(res->reason).append(") 错误原因: ")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            else if(resp.base_resp().success() != true){
                std::string err_desc;
                err_desc.append("get '/contact/find-all' 失败 ")
                        .append("错误原因: ")
                        .append(resp.base_resp().desc_str());
                throw ContactException(err_desc);
            }
            //到此是正常响应
            printContacts(resp);
        }
    private:
        //打印查询到的所有联系人
        void printContacts(const find_all_resp::FindAllResponce &resp)
        {
            cout << endl;
            cout << "-----当前所有联系人-----" << endl;
            for(int i = 0; i < resp.peoples_size(); ++i)
            {
                auto people = resp.peoples(i);
                cout << "联系人" << i + 1 << ": " + people.uuid() << " " << people.name() << endl;                         
            }
            cout << "-----------------------" << endl;
            cout << endl;
        }
        //创建联系人
        void buildContace(AddContactRequest &contact)
        {
            cout << "请输入联系人姓名: " ;
            std::string name;
            getline(cin, name);
            contact.set_name(name);
            cout << "请输入联系人年龄: ";
            int age;
            cin >> age;
            cin.ignore(256, '\n');
            contact.set_age(age);
            //可输入多个电话
            for(int i = 0; ; ++i)
            {
                cout << "请输入电话" << i + 1 << " (仅回车结束电话输入): ";
                std::string number;
                getline(cin, number); 
                if(number.empty()) break;
                //添加联系人电话
                AddContactRequest_Phone *phone = contact.add_phone();
                phone->set_number(number);

                cout << "号码类型(1.移动电话ML 2.固定电话TEL): ";
                int type;
                cin >> type;
                cin.ignore(256, '\n');
                switch (type)
                {
                case 1:
                    phone->set_type(AddContactRequest_Phone_PhoneType_MP);
                    break;
                case 2:
                    phone->set_type(AddContactRequest_Phone_PhoneType_TEL);
                    break;
                default:
                    cout << "选择有误" << std::endl;
                    break;
                } 
            }
            //备注信息
            for(int i = 0; ; ++i)
            {
                std::cout << "请输入备注信息" << i + 1 << " 标题(仅回车表示结束): ";
                std::string remark_info;
                getline(std::cin, remark_info);
                if(remark_info.empty())
                    break;
                std::cout << "请输入备注信息内容: ";
                std::string remark_message;
                getline(std::cin, remark_message);

                contact.mutable_remark()->insert({remark_info, remark_message});
            }
        }

        //打印删除的联系人信息
        void printDelContactInfo(const del_contact_resp::DelContactResponce &resp)
        {
            auto people = resp.people_info();
            cout << "uuid: " << people.uuid() << endl;
            cout << "姓名: " << people.name() << endl;
            cout << "年龄: " << people.age() << endl;
            //电话
            for(int i = 0; i < people.phone_size(); ++i)
            {
                auto phone = people.phone(i);
                cout << "电话" << i + 1 << ": " << phone.number() << " (";
                switch (phone.type())
                {
                case people_info::PeopleInfo_Phone_PhoneType_MP:
                    cout << "MP)" << endl;
                    break;
                case people_info::PeopleInfo_Phone_PhoneType_TEL:
                    cout << "TEL)" << endl;
                    break;
                default:
                    break;
                }
            }
            //备注信息
            for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it)
                cout << "备注信息标题: " << it->first << endl << "备注信息: " << it->second << endl;
        }
    };
}
